/******************************************************************************

	COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2010.

	This file is part of Maxsi Engine.

	Maxsi Engine is free software: you can redistribute it and/or modify it
	under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	Maxsi engine is distributed in the hope that it will be useful, but WITHOUT
	ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
	FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
	for more details.

	You should have received a copy of the GNU Lesser General Public License
	along with Maxsi Engine. If not, see <http://www.gnu.org/licenses/>.

	MaxsiEngine.dll
	A general purpose C++ library for the Maxsi Technology Project.

	MaxsiFileFormat.cpp
	A library for handling .mff files, a simple (compressed) achive format
	allowing people to store huge files and simple metadata. It's ridiculously
	simple to parse and could be easily extended, in fact, we urged you to,
	just send back your improvements and we'll add support. File format is
	released under the GNU LGPL.

******************************************************************************/

#if !defined(ME_MFFDisabled) || defined(ME_MFFEnabled)
#ifndef MaxsiFileFormat_H
#define MaxsiFileFormat_H

BeginMaxsiNamespace

//=============================================================================
//	Defines
//=============================================================================
#define MaxsiMFF_Flag_IsZLibCompressed		0x00000001
#define MaxsiMFF_Flag_HasSHA1Hash			0x00000002
#define MaxsiMFF_Flag_NotInitialized		0x80000000

#define MaxsiMFF_Device_UsingHandle			0x00000001 // Not set means using Buffer
#define MaxsiMFF_Device_CreatedHandle		0x00000002 // Set means we ceated the handle/buffer
#define MaxsiMFF_Device_CreatedBuffer		0x00000002 // Set means we ceated the handle/buffer
#define MaxsiMFF_Device_PretendFakeEOF		0x00000004 // Set means pretend the EOF is earlier than it actually is

#define MaxsiDevice_ESuccess				0x00000000
#define MaxsiDevice_EAllocFailed			0x00000001
#define MaxsiDevice_EWriteFailed			0x00000002
#define MaxsiDevice_EReadFailed				0x00000003
#define MaxsiDevice_EAddressSpaceTooSmall	0x00000004
#define MaxsiDevice_ENotEnoughDataAvailable	0x00000005
#define MaxsiDevice_ENotInitialized			0x00000006
#define MaxsiDevice_EReadOnly				0x00000007
#define MaxsiDevice_EInvalidParameters		0x00000008

#define MaxsiDevice_FStaticBuffer			0x00000001

//=============================================================================
//	Type definitions
//=============================================================================
typedef HANDLE MHANDLE;
typedef char TimeValue_tA[21];
typedef wchar_t TimeValue_tW[21];

//=============================================================================
//	Forward declarations
//=============================================================================
class MaxsiFileFormat;
class MaxsiFileFormatDevice;
class MaxsiFileFormatDeviceBuffer;
class MaxsiFileFormatHandler;
class MFFINFO;
class MFFSortie12Apr2010;

//=============================================================================
//	String redirection macros
//=============================================================================
#ifdef ME_USE_MULTIBYTE_STRING

	#define TimeValue_t TimeValue_tW

#else

	#define TimeValue_t TimeValue_tA

#endif

//=============================================================================
//	MFFINFO - A simple meta-data container for the Maxsi File Format enabled
//	programs able to use the virtual file system exposed by the MFF* functions.
//=============================================================================
CLASSLINK MFFINFO
{
public:
	MFFINFO();
	~MFFINFO();

public:
	//=============================================================================
	// wchar_t* FileName - The path to the file from the achive root in Unicode.
	//=============================================================================
	wchar_t*		FileName;

	//=============================================================================
	// FileSize_t FileSize - The size of the file when unpacked/uncompressed/read
	//=============================================================================
	FileSize_t		FileSize;

	//=============================================================================
	// FileSize_t FileDataSize - The size of the file within the memory. If the
	// file is compressed using ZLib, then this is the size of the compressed data
	// while FileSize is the size after decompression. Since (de)compression is
	// done automatically by the MFF subsystem, applications should not worry about
	// this at all. It should just worry about setting the flag whether it should
	// be compressed when writing the file.
	//=============================================================================
	FileSize_t		FileDataSize;

	//=============================================================================
	// UINT32 FileFlags - Various combinations of or'd MaxsiMFF_Flag_* flags! :D
	//=============================================================================
	UINT32			FileFlags;

	//=============================================================================
	// MESTR SHA1Hash[41] - A 40 character long string containing the SHA1 hash of
	// the file in question, if, and only if, the MaxsiMFF_Flag_HasSHA1Hash flag
	// is set in FileFlags. Otherwise undefined.
	//=============================================================================
	MESTR			SHA1Hash[41];

	//=============================================================================
	// TimeValue_t Created - A string containing the date of creation.
	//=============================================================================
	TimeValue_t		Created;

	//=============================================================================
	// TimeValue_t Modified - A string containing the date of the last edit.
	//=============================================================================
	TimeValue_t		Modified;

	//=============================================================================
	// size_t Internal - Reserved for use by the subsystem. MUST NOT be used by the
	// program unless defined by the Maxsi File Format Handler currently in use.
	//=============================================================================
	size_t			Internal;

};

//=============================================================================
//	MaxsiFileFormatDevice - A simple interface to all sorts of binary streams.
//=============================================================================
CLASSLINK MaxsiFileFormatDevice
{
public:
	MaxsiFileFormatDevice();
	~MaxsiFileFormatDevice();

public:
	virtual	int			Seek(FileSize_t Offset) = 0;
	virtual	int			Read(BYTE* Buffer, size_t BufferLen) = 0;
	virtual	int			Write(BYTE* Buffer, size_t BufferLen) = 0;
	virtual	int			SetSize(FileSize_t Size) = 0;
	virtual	FileSize_t	GetSize() = 0;

public:
	FileSize_t			Position;

};

//=============================================================================
//	MaxsiFileFormatDeviceBuffer - Loads and writes stuff to a buffer in memory.
//=============================================================================
CLASSLINK MaxsiFileFormatDeviceBuffer : public MaxsiFileFormatDevice
{
public:
	MaxsiFileFormatDeviceBuffer();
	~MaxsiFileFormatDeviceBuffer();

public:
	virtual	int			SetInput(BYTE** Pointer, size_t* Size);

public:
	virtual	int			Seek(FileSize_t Offset);
	virtual	int			Read(BYTE* Buffer, size_t BufferLen);
	virtual	int			Write(BYTE* Buffer, size_t BufferLen);
	virtual	int			SetSize(FileSize_t Size);
	virtual	FileSize_t	GetSize();

public:
	BYTE**				Data;
	size_t*				DataSize;
	int					Flags;

};

//=============================================================================
//	Maxsi File Format, a flexible simple interface to handling .mff files!
//=============================================================================
CLASSLINK MaxsiFileFormat
{
public:
	MaxsiFileFormat();
	~MaxsiFileFormat();

public:
	int			SetInputDevice(MaxsiFileFormatDeviceBuffer* Device);
	int			SetOutputDevice(MaxsiFileFormatDeviceBuffer* Device);
	int			SetDevice(MaxsiFileFormatDeviceBuffer* Device);

//public:
//	//=============================================================================
//	//	int InputFile(MESTR* Path, FileSize_t Begin, FileSize_t Length);
//	//
//	//	If Path is set, loads the virtual file system from the file Path [Starting
//	//	at the byte Begin [and pretends the file ends at Length to avoid buffer
//	//	overruns]]. If Path is NULL, but an Input device was set before calling
//	//	InputFile, then the virtual file system is loaded from the selected device.
//	//	Note: Calling InputFile causes the in-memory/on-disk file system to be
//	//	discarded and replaced by the file system from the new device.
//	//=============================================================================
//	int			InputFile(MESTR* Path);
//	int			InputFile(MESTR* Path, FileSize_t Begin);
//	int			InputFile(MESTR* Path, FileSize_t Begin, FileSize_t Length);
//
//public:
//	//=============================================================================
//	//	int OutputFile(MESTR* Path, FileSize_t Begin, FileSize_t Length);
//	//
//	//	If Path is set, saves the virtual file system to the file Path [Starting
//	//	at the byte Begin [and limits the output to Length bytes to prevent buffer
//	//	overruns]]. If Path is NULL, but an Output device was set before calling
//	//	OutputFile, then the virtual file system is saved to the selected device.
//	//=============================================================================
//	int			OutputFile(MESTR* Path);
//	int			OutputFile(MESTR* Path, FileSize_t Begin);
//	int			OutputFile(MESTR* Path, FileSize_t Begin, FileSize_t Length);

public:
	int			SetInputBehaviour(UINT32 Flags);
	int			SetOutputBehaviour(UINT32 Flags);

//public:
//	int			SetInputFile(MESTR* Path);
//	int			SetInputFile(MESTR* Path, FileSize_t Begin);
//	int			SetInputFile(MESTR* Path, FileSize_t Begin, FileSize_t Length);
//	int			SetInputFileSeek(FileSize_t Offset);
//	int			SetInputFileLength(FileSize_t Length);
//	int			SetInput(BYTE* Input, size_t DataLen);
//	int			SetInputExtern(BYTE* Input, size_t DataLen);
//	int			SetInputHandle(HANDLE Handle);
//
//public:
//	int			SetOutputFile(MESTR* Path);
//	int			SetOutputFile(MESTR* Path, FileSize_t Begin);
//	int			SetOutputFile(MESTR* Path, FileSize_t Begin, FileSize_t Length);
//	int			SetOutputFileSeek(FileSize_t Offset);
//	int			SetOutputFileLength(FileSize_t Length);
//	int			SetOutput(BYTE** Output, size_t* DataLen);
//	int			SetOutputExtern(BYTE* Output, size_t DataLen);
//	int			SetOutputHandle(HANDLE Handle);

public:
	UINT64		GetNumFiles();
	bool		GetNextFile(MFFINFO* FileInfo);

public:
	MHANDLE		MFFCloseFile(MHANDLE File);
	MHANDLE		MEFCloseAndDeleteFile(MHANDLE File);
	MHANDLE		MFFCreateFile(MESTR* Path, UINT32 FileFlags = MaxsiMFF_Flag_HasSHA1Hash);
	MHANDLE		MEFDeleteFile(MESTR* Path);
	MHANDLE		MFFOpenFile(MESTR* Path);

public:
	int			MEFSetFileSize(MHANDLE Handle);
	int			MEFSetFileSize(MHANDLE Handle, FileSize_t FileSize);

public:
	int			MFFReadFile(MHANDLE Handle, BYTE* Buffer, FileSize_t Length, size_t* Read = NULL);
	int			MFFReadFile(MHANDLE Handle, BYTE* Buffer, FileSize_t Length, FileSize_t Seek, size_t* Read = NULL);

public:
	int			MFFWriteFile(MHANDLE Handle, BYTE* Buffer, FileSize_t Length, size_t* Written = NULL);
	int			MFFWriteFile(MHANDLE Handle, BYTE* Buffer, FileSize_t Length, FileSize_t Seek, size_t* Written = NULL);

public:
	int			GetFileInfoPath(MESTR* Path, MFFINFO* Info);
	int			GetFileInfo(MHANDLE Handle, MFFINFO* Info);
	int			SetFileInfo(MHANDLE Handle, MFFINFO* Info);

public:
	FileSize_t	GetInputHeaderLength();

public:
	int			ReadInputDevice(BYTE* Buffer, size_t BufferSize, size_t* Read = NULL);
	int			ReadInputDevice(BYTE* Buffer, size_t BufferSize, FileSize_t Seek, size_t* Read = NULL);

public:
	int			ReadOutputDevice(BYTE* Buffer, size_t BufferSize, size_t* Read = NULL);
	int			ReadOutputDevice(BYTE* Buffer, size_t BufferSize, FileSize_t Seek, size_t* Read = NULL);
	int			WriteOutputDevice(BYTE* Buffer, size_t BufferSize, size_t* Written = NULL);
	int			WriteOutputDevice(BYTE* Buffer, size_t BufferSize, FileSize_t Seek, size_t* Written = NULL);

 public:
	bool		IOIsSameDevice();

public:
	MaxsiFileFormatHandler*			InputHandler;
	MaxsiFileFormatHandler*			OutputHandler;

public:
	MaxsiFileFormatDeviceBuffer*	InputDevice;
	MaxsiFileFormatDeviceBuffer*	OutputDevice;


//public:
//	UINT32		InputDeviceFlags; // Or'd combination of MaxsiMFF_Device_*
//	HANDLE		InputDeviceHandle;
//	FileSize_t	InputDeviceHandleOffset;
//	FileSize_t	InputDeviceHandleLength;
//	BYTE*		InputDeviceBuffer;
//	size_t		InputDeviceBufferLength;
//
//public:
//	UINT32		OutputDeviceFlags; // Or'd combination of MaxsiMFF_Device_*
//	HANDLE		OutputDeviceHandle;
//	FileSize_t	OutputDeviceHandleOffset;
//	FileSize_t	OutputDeviceHandleLength;
//	BYTE**		OutputDeviceBuffer;
//	size_t*		OutputDeviceBufferLength;
//	BYTE*		OutputDeviceExternBuffer;
//	size_t		OutputDeviceExternBufferLength;

public:
	bool		UsesInBuffer();
	bool		UsesInHandle();
	bool		UsesOutBuffer();
	bool		UsesOutHandle();
	bool		UsesOutBufferExtern();
	BYTE*		GetOutBuffer();

};

//=============================================================================
//	MaxsiFileFormatHandler - A parser for a given version of the Maxsi File
//	Format. Exposes a flexible synchronous interface for dealing with a virtual
//	File System embedded within a .mff file.
//=============================================================================
CLASSLINK MaxsiFileFormatHandler
{
public:
	MaxsiFileFormatHandler();
	~MaxsiFileFormatHandler();

public:
	virtual	bool		Parses(char* SpecificationCreator, char* SpecificationDate);

public:
	virtual	int			InputFile();
	virtual	int			OutputFile();

public:
	virtual	int			SetInputBehaviour(UINT32 Flags);
	virtual	int			SetOutputBehaviour(UINT32 Flags);

public:
	virtual	UINT64		GetNumFiles();
	virtual	bool		GetNextFile(MFFINFO* FileInfo);

public:
	virtual	MHANDLE		MFFCloseFile(MHANDLE File);
	virtual	MHANDLE		MEFCloseAndDeleteFile(MHANDLE File);
	virtual	MHANDLE		MFFCreateFile(MESTR* Path, UINT32 FileFlags);
	virtual	MHANDLE		MEFDeleteFile(MESTR* Path);
	virtual	MHANDLE		MFFOpenFile(MESTR* Path);

public:
	virtual	int			MEFSetFileSize(MHANDLE Handle);
	virtual	int			MEFSetFileSize(MHANDLE Handle, FileSize_t FileSize);

public:
	virtual	int			MFFReadFile(MHANDLE Handle, BYTE* Buffer, FileSize_t Length, FileSize_t Seek, size_t* Read);

public:
	virtual	int			MFFWriteFile(MHANDLE Handle, BYTE* Buffer, FileSize_t Length, FileSize_t Seek, size_t* Written);

public:
	virtual	int			GetFileInfoPath(MESTR* Path, MFFINFO* Info);
	virtual	int			GetFileInfo(MHANDLE Handle, MFFINFO* Info);
	virtual	int			SetFileInfo(MHANDLE Handle, MFFINFO* Info);

public:
	UINT32				InputBehaviourFlags;
	UINT32				OutputBehaviourFlags;

public:
	MaxsiFileFormat*	Context;

};

/*

Application/x-MaxsiFileFormat Specification by Sortie Version Apr 11 2010:
---------------------------------------------------------------------------

Application/x-MaxsiFileFormat\#00
\#06Sortie\#00
\#0BApr 11 2010\#00

\uint64 NumFiles;
\FileTable Table[NumFiles];
\File Files[NumFiles]

\typedef char[21] TimeValue; // Null-terminated string in the yyyy-mm-dd hh:mm::ss format

\struct SHA1Hash
\{
\int8 HasHash; // Whether the hash is valid or not.
\BYTE[20]; // Binary 160-bit hash value
\}

\struct FileTable
\{
\uint64 FileNameSize;
\wchar_t FileName[FileNameSize+1]; // Null terminated
\uint64 FileSize;
\uint64 FileLocation;
\sha1_t SHA1Hash;
\TimeValue Created;
\TimeValue Modified;
\uint64 ZLibCompressedSize; // 0 if not compressed
\};

\struct File
\{
\BYTE Data[FileSize];
\}

*/

/*

Application/x-MaxsiFileFormat Specification by Sortie Version Apr 12 2010:
---------------------------------------------------------------------------

Application/x-MaxsiFileFormat\#00
\#06Sortie\#00
\#0BApr 12 2010\#00

\uint64 NumFiles;
\FileTable Table[NumFiles];
\FileName_t FileNames[NumFiles];
\File Files[NumFiles]

\typedef char[21] TimeValue; // Null-terminated string in the yyyy-mm-dd hh:mm::ss format
\typedef FileName_t wchar_t[FileSize+1]; // Null-terminated string

\struct SHA1Hash
\{
\BYTE[20]; // Binary 160-bit hash value
\}

\struct FileTable
\{
\uint64 FileNameSize;
\uint64 FileNameLocation;
\uint64 FileDataSize;
\uint64 FileDataLocation;
\uint64 FileSize;
\TimeValue Created;
\TimeValue Modified;
\uint32 FileFlags;
\sha1_t SHA1Hash;
\};

\struct File
\{
\BYTE Data[FileSize];
\}

*/

EndMaxsiNamespace

#endif
#endif
